package com.google.code.sfa.tigr;

import java.util.*;
import java.io.IOException;
import com.google.code.sfa.parse.*;
import com.google.code.sfa.util.OverlapAlignment;

import org.biojava.bio.symbol.Alignment;
import org.biojava.bio.seq.Sequence;
import org.biojava.bio.alignment.SequenceAlignment;
import org.biojava.bio.alignment.SmithWaterman;
import org.biojava.bio.alignment.SubstitutionMatrix;
import org.biojava.bio.seq.DNATools;
import org.biojava.bio.BioException;

public class TigrAssembler {

    int queryStart, targetStart;

    public void assemble( List<Trace> traces, int nmerSize) throws IOException{
	
	TraceIndex index = new TraceIndex(  nmerSize);

	for( Trace trace : traces) {
	    index.add( trace);
	    /*trace.reverse();
	    index.add( trace);
	    trace.reverse();*/
	}
	for( Trace trace : traces) {
	    trace.setMatches( index.findMatches( trace));
	}
	Collections.sort( traces);
	float median = medianMatches( traces);
	float repeatThreshold = median * 1.5f;

	int i = traces.size() - 1;
	List<Trace> unusedNonRepeat = new ArrayList<Trace>();
	List<Trace> unusedRepeat = new ArrayList<Trace>();
	for( Trace t : traces) {
	    if( t.getMatchesSize() > repeatThreshold ) {
		t.setRepeat();
		unusedRepeat.add(t);
	    } else {
		unusedNonRepeat.add(t);
	    }
	}

	List<Contig> contigs = new ArrayList<Contig>();

	Collections.sort( unusedRepeat);
	Collections.sort( unusedNonRepeat);

	while(true) {
	    Trace seed;
	    if( unusedNonRepeat.size() > 0) {
		seed = unusedNonRepeat.remove( unusedNonRepeat.size() -1);
	    } else if( unusedRepeat.size() > 0) {
		seed = unusedRepeat.remove( unusedRepeat.size() - 1);
	    } else {
		break;
	    }

	    Contig contig = new Contig( seed);
	    				       
	    Trace potentialOverlap;
	    while( contig.hasNextPotentialOverlap() ) {
		
		potentialOverlap = contig.nextPotentialOverlap();
		if( potentialOverlap.isRepeat() && unusedRepeat.contains( potentialOverlap) ||
		    !potentialOverlap.isRepeat() && unusedNonRepeat.contains( potentialOverlap)) {

		    if( tryMerge( contig, potentialOverlap) ) {
			if( potentialOverlap.isRepeat() ) {
			    unusedRepeat.remove(potentialOverlap);
			} else {
			    unusedNonRepeat.remove(potentialOverlap);
			}
		    }
		    /*
		    potentialOverlap.reverse();
		    if( tryMerge( contig, potentialOverlap) ) {
			if( potentialOverlap.isRepeat() ) {
			    unusedRepeat.remove(potentialOverlap);
			} else {
			    unusedNonRepeat.remove(potentialOverlap);
			}
		    }
		    */
		}
	    }
	    System.out.println( contig.toString() );
	}
		
	
    }

    public boolean tryMerge( Contig contig, Trace potentialOverlap) {
	try {

	    OverlapAlignment a = new OverlapAlignment();
	    a.align( potentialOverlap.getSequence(), contig);

	    MatchCriteria mc = new MatchCriteria(50, 1000, 0.80, 4);
	    if( mc.testMatch( a) ) {
		contig.add( potentialOverlap, a);
	    }


	    //Alignment alignment = a.alignment;
	    //System.out.println( "Score: " + a.score + " Offset: " + a.offset);

	    //String subject = "", query = "";
	    
	    //if( a.score > 500) {
	    //	contig.add( potentialOverlap, a);
	    //}
	    /*
	    for( int i = 1; i <= alignment.length(); i++){
		subject += DNATools.dnaToken( alignment.symbolAt( "query", i));
		query   += DNATools.dnaToken( alignment.symbolAt( "subject", i));
	    }
	    System.out.println( subject);
	    System.out.println( query);
	    */
	    
	    /*
	    SubstitutionMatrix matrix = new SubstitutionMatrix( DNATools.getDNA(), (short) 10, (short) -10);
	    

	    SmithWaterman aligner = new SmithWaterman(
						      (short) -10,     // match
						      (short) 10,      // replace 
						      (short) 10,      // insert
						      (short) 10,      // delete
						      (short) 10,      // gapExtend
						      matrix  // SubstitutionMatrix
						      );
	    // Perform the local alignment.
	    //int editDistance = aligner.pairwiseAlignment(potentialOverlap.getSequence(), contig);
	    Alignment alignment = aligner.getAlignment(potentialOverlap.getSequence(), contig);
	    String subject = "";
	    String query = "";
	    for( int i = 1; i <= alignment.length(); i++){
		subject += DNATools.dnaToken( alignment.symbolAt( "query", i));
		query   += DNATools.dnaToken( alignment.symbolAt( "subject", i));
	    }
	    System.out.println( subject);
	    System.out.println( query);
	    parseAlignmentOutput( aligner.getAlignmentString());
	    System.out.println( "queryStart: " + queryStart);
	    System.out.println( "targetStart: " + targetStart);
	    //System.out.println("\nEdit Distance: " + editDistance);
	    System.out.println("\nlocal alignment with SmithWaterman:\n" + aligner.getAlignmentString());
	    */
	    return true;
	} catch( Exception e) {
	    e.printStackTrace();
	    return false;
	}
    }

    public void parseAlignmentOutput( String alignmentString) {
	String[] lines = alignmentString.split("\n");
	queryStart = -1;
	targetStart = -1;
	for( int i = 0; i < lines.length; i++) {
	    if( lines[i].startsWith( "Query:")) {
		queryStart = extractNumber( lines[i]);
	    } else if( lines[i].startsWith("Target:")) {
		targetStart = extractNumber( lines[i]);
	    }

	    if( queryStart != -1 && targetStart != -1) {
		return;
	    }
	}
    }

    public int extractNumber( String line) {
	String[] cols = line.split(" +");
	try {
	    return Integer.parseInt( cols[1]);
	} catch( Exception e) {
	    return -1;
	}
    }

    public float medianMatches( List<Trace> traces) {
	int length = traces.size();
	if( length % 2 == 0) {
	    return (traces.get( length / 2 - 1).getMatchesSize() + 
		    traces.get( length / 2    ).getMatchesSize() ) / 2.0f;
	} else {
	    return (float) traces.get( length / 2).getMatchesSize();
	}
    }

    


}